use std::collections::HashMap;
use std::future::{ready, Ready};

use crate::models::state::AppState;
use crate::models::{user, response};
use crate::base::errors::RBlogError;
use crate::utils::auth_util::{generate_user_id, verify};

use actix_identity::Identity;
use actix_web::{web, HttpResponse, FromRequest, Error, HttpRequest, dev::Payload, HttpMessage};
use actix_session::Session;
use log::error;

use user::{AuthData, SlimUser};

use serde::{Deserialize, Serialize};
use crate::models::user::RegisterData;
use crate::dbaccess::user_access::user_register_access;

pub type LoggedUser = SlimUser;


impl FromRequest for LoggedUser {
    type Error = Error;
    type Future = Ready<Result<LoggedUser, Error>>;

    fn from_request(req: &HttpRequest, pl: &mut Payload) -> Self::Future {
        if let Ok(identity) = Identity::from_request(req, pl).into_inner() {
            if let Ok(user_json) = identity.id() {
                if let Ok(user) = serde_json::from_str(&user_json) {
                    return ready(Ok(user));
                }
            }
        }

        ready(Err(RBlogError::Unauthorized("Unauthorized".to_string()).into()))
    }
}


pub async fn auth_login_handler(
    mut app_state: web::Data<AppState>,
    request: HttpRequest, auth_info: web::Json<AuthData>
)
    -> Result<HttpResponse, RBlogError> {
    let verify_res = verify(&mut app_state.pool.clone(),
                                  &auth_info.username,&
                                      auth_info.password).await;
    if verify_res {
        let logger_user = LoggedUser{
            username: Some(auth_info.clone().username)
        };
        let logger_string = serde_json::to_string(&logger_user)?;
        Identity::login(&request.extensions(), logger_string).unwrap();
        Ok(HttpResponse::Ok().json(
            response::ResponseResult::<bool>::success_without_data()))

    } else {
        Ok(HttpResponse::Forbidden().json(
            response::ResponseResult::<bool>::fail(
                "username or password invalid".to_string())))
    }
}


pub async fn logout(id: Identity) -> Result<HttpResponse, RBlogError> {
    id.logout();
    Ok(HttpResponse::Ok().json(
        response::ResponseResult::<bool>::success_without_data())
    )
}

pub async fn get_me(
    logged_user: LoggedUser) -> Result<HttpResponse, RBlogError> {
    Ok(HttpResponse::Ok().json(logged_user))
}


pub async fn user_register(
    mut app_state: web::Data<AppState>,
    register_data: web::Json<RegisterData>,
) ->  Result<HttpResponse, RBlogError>{
   user_register_access(
        &mut app_state.pool.clone(),
        register_data.into_inner())
       .await
       .map(|register_res| HttpResponse::Ok().json(register_res))
}